/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2003-2008 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"
#if defined (UMC_ENABLE_MP3_INT_AUDIO_DECODER)

#include "mp3dec_own_int.h"

MP3Status mp3idecUpdateMemMap(MP3Dec *state, Ipp32s shift)
{
  Ipp32s i;
  mp3decUpdateMemMap_com(&state->com, shift);

  for (i = 0; i < 34; i++) {
    MP3_UPDATE_PTR(void, state->com.huff_table[i].phuftable, shift)
  }

  MP3_UPDATE_PTR(sampleint, state->smpl_xr, shift)
  MP3_UPDATE_PTR(sampleint, state->smpl_ro, shift)
  MP3_UPDATE_PTR(sampleint, state->smpl_re, shift)
  MP3_UPDATE_PTR(sampleintrw, state->smpl_rw, shift)
  MP3_UPDATE_PTR(sampleshort, state->smpl_sb, shift)
  MP3_UPDATE_PTR(sampleshort, state->com.smpl_xs, shift)

  return MP3_OK;
}

MP3Status mp3idecInit(MP3Dec *state, Ipp32s synchro_mode, Ipp32s *size_all)
{
    Ipp32s ts, size;
    Ipp8u *mem;

    if (synchro_mode < 0 || synchro_mode > 1) {
      return MP3_FAILED_TO_INITIALIZE;
    }

    size = 0;
    mem = 0;

    ts = sizeof(MP3Dec);
    size += ts;
    if (state)
      mem = (Ipp8u *)state + ts;

    mp3decGetSize_com(&ts);
    size += ts;
    if (mem) {
      mp3decInit_com(&state->com, mem);
      mem += ts;
    }

    mp3idec_initialize_huffman(mem ? state->com.huff_table : 0, mem, &ts);
    size += ts;
    if (mem) mem += ts;

    if (state) {
      ippsZero_8u((Ipp8u *)(state->tmp_bfr), 32 * sizeof(Ipp32f));
      ippsZero_8u((Ipp8u *)(state->filter_bfr), 2 * 64 * 16 * sizeof(Ipp32f));

      ippsZero_8u((Ipp8u *)(state->global), 5760 * sizeof(Ipp32u));
      ippsZero_8u((Ipp8u *)(state->prevblk), 2 * 576 * sizeof(Ipp32f));

      /* out of subband synth */
      state->smpl_sb = (sampleshort *) state->global;
      /* out of dequantizer */
      state->smpl_xr =
      /* out of reordering */
      state->smpl_ro =
      /* out of antialiasing */
      state->smpl_re = (sampleint *) state->smpl_sb;
      /* out of imdct */
      state->smpl_rw = (sampleintrw *) ((Ipp8s *)(state->global) + sizeof(sampleint));
      /* out of huffman */
      state->com.smpl_xs = (sampleshort *) ((Ipp8s *)(state->global) +
          sizeof(sampleint) * 2);

      ippsZero_8u((Ipp8u *)(state->m_ptr), 2 * 2 * sizeof(Ipp16s));
      state->m_even[0] = state->m_even[1] = 0;

      state->synth_ind[0] = state->synth_ind[1] = 0;
      ippsZero_8u((Ipp8u *)(state->synth_buf), IPP_MP3_V_BUF_LEN * 2 * sizeof(Ipp32s));
      state->dctnum_prev[0] = state->dctnum_prev[1] = 0;

      state->com.synchro_mode = synchro_mode;
    }

    if(size_all)
      *size_all = size;

    return MP3_OK;
}

MP3Status mp3idecReset(MP3Dec *state)
{
    mp3decReset_com(&state->com);

    ippsZero_8u((Ipp8u *)(state->tmp_bfr), 64 * sizeof(Ipp32f));
    ippsZero_8u((Ipp8u *)(state->filter_bfr), 2 * 64 * 16 * sizeof(Ipp32f));

    ippsZero_8u((Ipp8u *)(state->global), 5760 * sizeof(Ipp32u));
    ippsZero_8u((Ipp8u *)(state->prevblk), 2 * 576 * sizeof(Ipp32f));

    ippsZero_8u((Ipp8u *)(state->m_ptr), 2 * 2 * sizeof(Ipp16s));
    state->m_even[0] = state->m_even[1] = 0;

    return MP3_OK;
}

MP3Status mp3idecClose(MP3Dec *state)
{
    if (state == NULL)
        return MP3_OK;

    mp3decClose_com(/*&state->com*/);

    return MP3_OK;
}


MP3Status mp3idecGetFrame(Ipp8u  *inPointer,
                          Ipp32s inDataSize,
                          Ipp32s *decodedBytes,
                          Ipp16s *outPointer,
                          Ipp32s outBufferSize,
                          MP3Dec *state)
{
    MP3Status res;
    Ipp32s prev_decodedBytes = state->com.decodedBytes;
    Ipp32s frameSize;

    IppMP3FrameHeader *header = &(state->com.header);

    if (!inPointer || !outPointer)
        return MP3_NULL_PTR;
    res = mp3dec_GetID3Len(inPointer, inDataSize, &(state->com));

    if (res != MP3_OK)
      return res;

    res = mp3dec_SkipID3(inDataSize, decodedBytes, &(state->com));

    if (res != MP3_OK) {
      return res;
    } else {
      inDataSize -= *decodedBytes;
      inPointer += *decodedBytes;
    }

    if (inDataSize == 0 && (state->com.m_StreamData.nDataLen <= state->com.decodedBytes + 32))
        return MP3_NOT_ENOUGH_DATA;
    state->com.m_pOutSamples = (Ipp16s *)outPointer;

    do {
        res = mp3dec_ReceiveBuffer(&(state->com.m_StreamData), inPointer, inDataSize);
        res = mp3dec_GetSynch(&state->com);
        if (res == MP3_NOT_FIND_SYNCWORD || res == MP3_BAD_STREAM || res == MP3_UNSUPPORTED) {
            *decodedBytes += state->com.decodedBytes - prev_decodedBytes;
            return res;
        } else if (res == MP3_NOT_ENOUGH_DATA) {
            *decodedBytes += state->com.decodedBytes - prev_decodedBytes;
            return res;
        }

        mp3idecGetFrameSize(&frameSize, state);

        if (outBufferSize < frameSize << (state->com.stereo - 1)) {
            return MP3_NOT_ENOUGH_BUFFER;
        }

        //CMC END
        state->com.m_frame_num++;

        res = MP3_OK;

        switch (header->layer) {
        case 3:
            if (header->id)
                mp3dec_audio_data_LayerIII(&state->com);
            else
                mp3dec_audio_data_LSF_LayerIII(&state->com);
            res = mp3idec_decode_data_LayerIII(state);
            break;
        case 2:
            if (state->com.nbal_alloc_table) {
              if (mp3dec_audio_data_LayerII(&state->com)) {
                res = MP3_NOT_FIND_SYNCWORD;
                state->com.decodedBytes -= state->com.MP3nSlots + 3;
                break;
              }
              mp3idec_decode_data_LayerII(state);
            }
            break;
        case 1:
            mp3dec_audio_data_LayerI(&state->com);
            mp3idec_decode_data_LayerI(state);
            break;

        default:
            res = MP3_UNSUPPORTED;  // unsupported layer
        }
        if (res == MP3_NOT_FIND_SYNCWORD) {
          Ipp32s len = state->com.decodedBytes - prev_decodedBytes;
          *decodedBytes += len;
          inPointer += len;
          inDataSize -= len;
          prev_decodedBytes = state->com.decodedBytes;
        }
    } while (res == MP3_NOT_FIND_SYNCWORD);

    *decodedBytes += (state->com.decodedBytes - prev_decodedBytes);

    ippsCopy_8u((Ipp8u *)&state->com.header, (Ipp8u *)&state->com.header_good,
        sizeof(IppMP3FrameHeader));
    state->com.mpg25_good = state->com.mpg25;
    state->com.m_bInit = 1;

    return res;
}

MP3Status mp3idecGetInfo(cAudioCodecParams *a_info, MP3Dec *state)
{
    IppMP3FrameHeader *header;
    cAudioStreamType mpeg_type[] = { MPEG2_AUD, MPEG1_AUD };
    cAudioStreamType layer_type[] =
    { MPEG_AUD_LAYER1, MPEG_AUD_LAYER2, MPEG_AUD_LAYER3 };
    Ipp32s ch, ch_mask;

    if (!a_info)
        return MP3_NULL_PTR;

    header = &(state->com.header_good);

    a_info->m_SuggestedInputSize = 4096;
    //a_info->m_SuggestedOutputSize = 1152 * 2 * sizeof(Ipp16s);
    a_info->m_SuggestedOutputSize = 1152 * (NUM_CHANNELS + 1) * sizeof(Ipp16s);

    if (state->com.m_bInit) {
        a_info->m_info_in.bitPerSample = 0;
        a_info->m_info_out.bitPerSample = 16;

        a_info->m_info_in.bitrate =
            mp3_bitrate[header->id][3 - header->layer][header->bitRate] * 1000;
        a_info->m_info_out.bitrate = 0;

        mp3idecGetChannels(&ch, &ch_mask, state);
        a_info->m_info_in.channels = ch;
        a_info->m_info_out.channels = ch;
        a_info->m_info_in.channel_mask = ch_mask;
        a_info->m_info_out.channel_mask = ch_mask;

        a_info->m_info_in.sample_frequency =
            mp3_frequency[header->id + state->com.mpg25_good][header->samplingFreq];
        a_info->m_info_out.sample_frequency =
            mp3_frequency[header->id + state->com.mpg25_good][header->samplingFreq];

        a_info->m_info_in.stream_type = (cAudioStreamType)
            (mpeg_type[header->id] | layer_type[header->layer - 1]);
        a_info->m_info_out.stream_type = PCM_AUD;

        a_info->m_frame_num = state->com.m_frame_num;

        a_info->is_valid = 1;

        return MP3_OK;
    }

    a_info->is_valid = 0;

    return MP3_OK;
}

MP3Status mp3idecGetDuration(Ipp32f *p_duration, MP3Dec *state)
{
    Ipp32f duration;
    Ipp32s frameSize;
    IppMP3FrameHeader *header;

    if (!state)
        return MP3_NULL_PTR;

    if (state->com.m_bInit)
      header = &(state->com.header_good);
    else
      header = &(state->com.header);

    mp3idecGetFrameSize(&frameSize, state);
    duration = (Ipp32f)state->com.m_frame_num * frameSize;

    duration /= (Ipp32f)mp3_frequency[header->id + state->com.mpg25_good][header->samplingFreq];

    *p_duration = duration;

    return MP3_OK;
}

MP3Status mp3idecGetChannels(Ipp32s *ch, Ipp32s *ch_mask, MP3Dec *state)
{
    IppMP3FrameHeader *header;
    Ipp32s ch_table[] = { 2, 2, 2, 1 };

    if (!state)
        return MP3_NULL_PTR;

    if (state->com.m_bInit)
      header = &(state->com.header_good);
    else
      header = &(state->com.header);

    *ch = ch_table[header->mode];

    if (*ch == 2)
      *ch_mask = MP3_CHANNEL_STEREO;
    else
      *ch_mask = MP3_CHANNEL_CENTER;

    return MP3_OK;
}

MP3Status mp3idecGetFrameSize(Ipp32s *frameSize, MP3Dec *state)
{
    IppMP3FrameHeader *header;
    Ipp32s fs[2][4] = {
        { 0, 384, 1152,  576 },
        { 0, 384, 1152, 1152 }
    };

    if (!state)
        return MP3_NULL_PTR;

    if (state->com.m_bInit)
      header = &(state->com.header_good);
    else
      header = &(state->com.header);

    *frameSize = fs[header->id][header->layer];

    return MP3_OK;
}

MP3Status mp3idecGetSampleFrequency(Ipp32s *freq, MP3Dec *state)
{
    IppMP3FrameHeader *header;

    if (!state)
        return MP3_NULL_PTR;

    if (state->com.m_bInit)
      header = &(state->com.header_good);
    else
      header = &(state->com.header);

    *freq = mp3_frequency[header->id + state->com.mpg25_good][header->samplingFreq];

    return MP3_OK;
}

#endif //UMC_ENABLE_MP3_INT_AUDIO_DECODER
